<?php
/**
 * Created by PhpStorm.
 * User: lzy
 * Date: 2018/11/20
 * Time: 18:10
 */
namespace App\Pay\Repositories;

use App\Pay\Exceptions\PayException;

/**
 * 微信支付类
 * Class DbWeixinPayRepositories
 * @package App\Pay\Repositories
 */
class DbWeixinPayRepositories{


    private  $values;
    /**
     * 获取appid;
     * @return string
     */
    public function getAppId(){
        return get_config_param_value('XIAO_APP_ID');
    }

    public function getMchId(){
        return get_config_param_value('XIAO_MCH_ID');
    }

    public function checkNotity($xml){
        $return_data = $this->xmlToArray($xml);
        $this->values=array();
        $this->values = $return_data;
        $sign=$return_data["sign"];
        $s =$this->MakeSign();
        if($sign!=$s){
            throw  new PayException(PayException::EROOR_PAY_SIGN_FAIL);
        }
        if ($return_data["return_code"] == "FAIL") {
              throw  new PayException(PayException::EROOR_PAY_NOTITY_FAIL);
        }else if ($return_data["return_code"] == "SUCCESS") {
              return $return_data;
        }else{
              throw  new PayException(PayException::EROOR_PAY_NOTITY_FAIL);
        }
    }

    public function getWeixinJsPay($paySn,$amount,$openid,$notify_url,$body=""){
            $appId = $this->getAppId();  //微信小程序appid
            $mchId = $this->getMchId();  //支付商户号
            //获取openid
            $url="https://api.mch.weixin.qq.com/pay/unifiedorder";
            //用户openid
            //$notify_url=get_config_param_value('XIAO_WXPAY_MONTH_NOTIFY');//config("lzy.wxpay_notify");  //支付成功后通知地址
            $onoce_str = $this->getRandChar(32);
            $data=array();
            $data["appid"] = $appId;
            $data["openid"]=$openid;
            $data["body"] =$body;
            $data["mch_id"] = $mchId;
            $data["nonce_str"] = $onoce_str;
            $data["notify_url"] = $notify_url;
            $data["out_trade_no"] = $paySn;
            $data["spbill_create_ip"] = $this->get_client_ip();
            $data["total_fee"] = $amount*100;
            $data["trade_type"] = "JSAPI";
            $s = $this->getSign($data, false);
            $data["sign"] = $s;
            $xml = $this->arrayToXml($data);
            $response = $this->postXmlCurl($xml, $url,TRUE);
            $result=$this->xmlstr_to_array($response);

            if ($result["return_code"] == "SUCCESS") {
                if (!empty($result["prepay_id"])) {
                    //生成订单号到数据库中去
                      return $this->getOrder($result['prepay_id']);
                } else {
                    throw  new PayException(PayException::PAY_FETCH_PRICE_ERROR,['msg'=>$result['err_code_des']]);
                }
            }else{
                throw  new PayException(PayException::PAY_FETCH_PRICE_ERROR,['msg'=>$result['err_code_des']]);


            }

    }



    //执行第二次签名，才能返回给客户端使用
    public function getOrder($prepayId) {
        $this->values=array();
        $this->values["appId"] = $this->getAppId();
        $this->values["timeStamp"] = "".time()."";
        $this->values["nonceStr"] =$this->getNonceStr();
        $this->values["package"] = "prepay_id=".$prepayId;
        $this->values["signType"] = "MD5";
        $this->values["paySign"] = $this->MakeSign();
        return $this->values;
    }
    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public function MakeSign()
    {
        //签名步骤一：按字典序排序参数
        ksort($this->values);
        $string = $this->ToUrlParams();
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=".$this->getPayKey();
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }
    /**
     * 格式化参数格式化成url参数
     */
    public function ToUrlParams()
    {
        $buff = "";
        foreach ($this->values as $k => $v)
        {
            if($k != "sign" && $v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }
    /**
     *
     * 产生随机字符串，不长于32位
     * @param int $length
     * @return 产生的随机字符串
     */
    public static function getNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {
            $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }
    /**
     * 以post方式提交xml到对应的接口url
     *
     * @param string $xml  需要post的xml数据或者是data 数据
     * @param string $url  url
     * @param bool $useCert 是否需要证书，默认不需要
     * @param int $second   url执行超时时间，默认30s
     * @throws WxPayException
     */
    private  function postXmlCurl($xml, $url, $useCert = false, $second = 30) {

        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);

        //如果有配置代理这里就设置代理
//        if (\WxConfig::CURL_PROXY_HOST != "0.0.0.0" && \WxConfig::CURL_PROXY_PORT != 0) {
//            curl_setopt($ch, CURLOPT_PROXY, \WxConfig::CURL_PROXY_HOST);
//            curl_setopt($ch, CURLOPT_PROXYPORT, \WxConfig::CURL_PROXY_PORT);
//        }
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if ($useCert == true) {
            //设置证书
            //使用证书：cert 与 key 分别属于两个.pem文件
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');

            curl_setopt($ch, CURLOPT_SSLCERT, $this->getApiclientCert());
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, $this->getApiclientKey());
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        curl_close($ch);
        //返回结果
        if ($data) {
            return $data;
        } else {
            $error = curl_errno($ch);
            throw new \Exception("curl出错，错误码:$error");
        }
    }

    /*
      生成签名
     */

    function getSign($Obj) {
        foreach ($Obj as $k => $v) {
            $Parameters[strtolower($k)] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //echo "【string】 =".$String."</br>";
        //签名步骤二：在string后加入KEY
        $String = $String . "&key=".$this->getPayKey();
        //echo "<textarea style='width: 50%; height: 150px;'>$String</textarea> <br />";
        //签名步骤三：MD5加密
        $result_ = strtoupper(md5($String));
        return $result_;
    }

    //获取指定长度的随机字符串
    function getRandChar($length) {
        $str = null;
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        $max = strlen($strPol) - 1;

        for ($i = 0; $i < $length; $i++) {
            $str .= $strPol[rand(0, $max)]; //rand($min,$max)生成介于min和max两个数之间的一个随机整数
        }

        return $str;
    }

    //数组转xml
    function arrayToXml($arr) {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
        }
        $xml .= "</xml>";
        return $xml;
    }


    /*
      获取当前服务器的IP
     */

    function get_client_ip() {

        return getClientIp();
    }

    //将数组转成uri字符串
    function formatBizQueryParaMap($paraMap, $urlencode) {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if ($urlencode) {
                $v = urlencode($v);
            }
            $buff .= strtolower($k) . "=" . $v . "&";
        }
        $reqPar="";
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }

    /**
    xml转成数组
     */
    function xmlstr_to_array($xmlstr) {
        $doc = new \DOMDocument();
        $doc->loadXML($xmlstr);
        return $this->domnode_to_array($doc->documentElement);
    }

    function domnode_to_array($node) {
        $output = array();
        switch ($node->nodeType) {
            case XML_CDATA_SECTION_NODE:
            case XML_TEXT_NODE:
                $output = trim($node->textContent);
                break;
            case XML_ELEMENT_NODE:
                for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
                    $child = $node->childNodes->item($i);
                    $v = $this->domnode_to_array($child);
                    if (isset($child->tagName)) {
                        $t = $child->tagName;
                        if (!isset($output[$t])) {
                            $output[$t] = array();
                        }
                        $output[$t][] = $v;
                    } elseif ($v) {
                        $output = (string) $v;
                    }
                }
                if (is_array($output)) {
                    if ($node->attributes->length) {
                        $a = array();
                        foreach ($node->attributes as $attrName => $attrNode) {
                            $a[$attrName] = (string) $attrNode->value;
                        }
                        $output['@attributes'] = $a;
                    }
                    foreach ($output as $t => $v) {
                        if (is_array($v) && count($v) == 1 && $t != '@attributes') {
                            $output[$t] = $v[0];
                        }
                    }
                }
                break;
        }
        return $output;
    }


    /**
     * xml 转成数组
     * @param $xml
     * @return mixed
     */
    function xmlToArray($xml) {
        //将XML转为array
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }

    /***
     * 获取证书路径
     * @return mixed
     */
    public function getApiclientCert(){
        return  storage_path()."weixin/cert/apiclient_cert.pem"; // /home/www/root/api.5v5.com/storage/wechat/apiclient_cert.pem
    }
    /**
     * 获取证书钥匙路径
     * @return mixed
     */
    public function getApiclientKey(){
        return  storage_path()."weixin/cert/apiclient_key.pem"; ///home/www/root/api.5v5.com/storage/wechat/apiclient_key.pem
    }

    /**
     * 支付key
     * @return \Illuminate\Config\Repository|mixed
     */
    public function getPayKey(){
        return  get_config_param_value('XIAO_APP_PAY_KEY');  //"35f7d99d3e27272789c05660d0312914";
    }

}